// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <nebula/types/fundamental.h>

namespace nebula {

    /// \brief Base class for all variable-size list data types
    class TURBO_EXPORT BaseListType : public NestedType {
    public:
        using NestedType::NestedType;

        // This is only for preventing defining this class in each
        // translation unit to avoid one-definition-rule violation.
        ~BaseListType() override = default;

        const std::shared_ptr<Field> &value_field() const { return children_[0]; }

        const std::shared_ptr<DataType> &get_value_type() const { return children_[0]->type(); }
    };

    /// \brief Concrete type class for list data
    ///
    /// List data is nested data where each value is a variable number of
    /// child items.  Lists can be recursively nested, for example
    /// list(list(int32)).
    class TURBO_EXPORT ListType : public BaseListType {
    public:
        static constexpr Type::type type_id = Type::LIST;
        using offset_type = int32_t;

        static constexpr const char *type_name() { return "list"; }

        // List can contain any other logical value type
        explicit ListType(std::shared_ptr<DataType> value_type)
                : ListType(std::make_shared<Field>("item", std::move(value_type))) {}

        explicit ListType(std::shared_ptr<Field> value_field) : BaseListType(type_id) {
            children_ = {std::move(value_field)};
        }

        DataTypeLayout layout() const override {
            return DataTypeLayout(
                    {DataTypeLayout::Bitmap(), DataTypeLayout::FixedWidth(sizeof(offset_type))});
        }

        std::string to_string(bool show_metadata = false) const override;

        std::string name() const override { return "list"; }

    protected:
        std::string compute_fingerprint() const override;
    };

    /// \brief Concrete type class for large list data
    ///
    /// LargeListType is like ListType but with 64-bit rather than 32-bit offsets.
    class TURBO_EXPORT LargeListType : public BaseListType {
    public:
        static constexpr Type::type type_id = Type::LARGE_LIST;
        using offset_type = int64_t;

        static constexpr const char *type_name() { return "large_list"; }

        // List can contain any other logical value type
        explicit LargeListType(std::shared_ptr<DataType> value_type)
                : LargeListType(std::make_shared<Field>("item", std::move(value_type))) {}

        explicit LargeListType(std::shared_ptr<Field> value_field) : BaseListType(type_id) {
            children_ = {std::move(value_field)};
        }

        DataTypeLayout layout() const override {
            return DataTypeLayout(
                    {DataTypeLayout::Bitmap(), DataTypeLayout::FixedWidth(sizeof(offset_type))});
        }

        std::string to_string(bool show_metadata = false) const override;

        std::string name() const override { return "large_list"; }

    protected:
        std::string compute_fingerprint() const override;
    };

    /// \brief Type class for array of list views
    class TURBO_EXPORT ListViewType : public BaseListType {
    public:
        static constexpr Type::type type_id = Type::LIST_VIEW;
        using offset_type = int32_t;

        static constexpr const char *type_name() { return "list_view"; }

        // ListView can contain any other logical value type
        explicit ListViewType(const std::shared_ptr<DataType> &value_type)
                : ListViewType(std::make_shared<Field>("item", value_type)) {}

        explicit ListViewType(const std::shared_ptr<Field> &value_field)
                : BaseListType(type_id) {
            children_ = {value_field};
        }

        DataTypeLayout layout() const override {
            return DataTypeLayout({DataTypeLayout::Bitmap(),
                                   DataTypeLayout::FixedWidth(sizeof(offset_type)),
                                   DataTypeLayout::FixedWidth(sizeof(offset_type))});
        }

        std::string to_string(bool show_metadata = false) const override;

        std::string name() const override { return "list_view"; }

    protected:
        std::string compute_fingerprint() const override;
    };

    /// \brief Concrete type class for large list-view data
    ///
    /// LargeListViewType is like ListViewType but with 64-bit rather than 32-bit offsets and
    /// sizes.
    class TURBO_EXPORT LargeListViewType : public BaseListType {
    public:
        static constexpr Type::type type_id = Type::LARGE_LIST_VIEW;
        using offset_type = int64_t;

        static constexpr const char *type_name() { return "large_list_view"; }

        // LargeListView can contain any other logical value type
        explicit LargeListViewType(const std::shared_ptr<DataType> &value_type)
                : LargeListViewType(std::make_shared<Field>("item", value_type)) {}

        explicit LargeListViewType(const std::shared_ptr<Field> &value_field)
                : BaseListType(type_id) {
            children_ = {value_field};
        }

        DataTypeLayout layout() const override {
            return DataTypeLayout({DataTypeLayout::Bitmap(),
                                   DataTypeLayout::FixedWidth(sizeof(offset_type)),
                                   DataTypeLayout::FixedWidth(sizeof(offset_type))});
        }

        std::string to_string(bool show_metadata = false) const override;

        std::string name() const override { return "large_list_view"; }

    protected:
        std::string compute_fingerprint() const override;
    };

    /// \brief Concrete type class for fixed size list data
    class TURBO_EXPORT FixedSizeListType : public BaseListType {
    public:
        static constexpr Type::type type_id = Type::FIXED_SIZE_LIST;
        // While the individual item size is 32-bit, the overall data size
        // (item size * list length) may not fit in a 32-bit int.
        using offset_type = int64_t;

        static constexpr const char *type_name() { return "fixed_size_list"; }

        // List can contain any other logical value type
        FixedSizeListType(std::shared_ptr<DataType> value_type, int32_t list_size)
                : FixedSizeListType(std::make_shared<Field>("item", std::move(value_type)),
                                    list_size) {}

        FixedSizeListType(std::shared_ptr<Field> value_field, int32_t list_size)
                : BaseListType(type_id), list_size_(list_size) {
            children_ = {std::move(value_field)};
        }

        DataTypeLayout layout() const override {
            return DataTypeLayout({DataTypeLayout::Bitmap()});
        }

        std::string to_string(bool show_metadata = false) const override;

        std::string name() const override { return "fixed_size_list"; }

        int32_t list_size() const { return list_size_; }

    protected:
        std::string compute_fingerprint() const override;

        int32_t list_size_;
    };
}  // namespace nebula
